#include "Networking/session.h"
#include "Networking/documentmanager.h"
#include "Change/documentmessage.h"
#include "Change/textchange.h"
#include "Editor/formattededitor.h"
#include "Editor/texteditor.h"
#include "MainWindow/mainwindow.h"
#include "UserHighlighter/userhighlighter.h"
#include "WelcomeScreen/welcomescreen.h"

#include <QMessageBox>
#include <QInputDialog>
#include <QObject>

DocumentManager::DocumentManager(Session *newSession)
{
	session = newSession;
}

void DocumentManager::receiveChange(QByteArray msg)
{
	Message::opType op = Message::getOpType(msg);

	if (op == Message::DOCUMENT)
	{
		DocumentMessage docMsg(msg);

		qDebug("Client received document info for doc #%d", docMsg.getDocId());

		//if the change is non-local we need to add it
		if (docMsg.getCreatorId() != session->getUserManager()->getLocalUser().getId())
		{
			if (docMsg.getDocType() == DocumentMessage::TEXT)
				addNewDocumentToList(Editor::TEXT, docMsg.getTabName());
			else
				addNewDocumentToList(Editor::FORMATTED, docMsg.getTabName());
		}
	}
	else if (op == Message::ADD_TEXT || op == Message::REMOVE || op == Message::CHANGE_FORMAT || op == Message::ADD_TABLE ||
			 op == Message::ADD_IMAGE || op == Message::ADD_HTML)
	{
		Change txtMsg(msg);

		if (txtMsg.getDocId() >= docList.size() || txtMsg.getDocId()  < 0)
		{
			qDebug("Bad Error! Change recieved for a document that does not exist locally");
			return;
		}
		else
		{
			Editor *currDoc = docList.at(txtMsg.getDocId());
			currDoc->receiveChange(msg);
		}
	}
	else if (op == Message::INITDOC)	//Document initialisation
	{

	}

}

void DocumentManager::sendChange(QByteArray changeData)
{
	session->sendMessage(changeData);
}

int DocumentManager::getLocalUserId()
{
	return session->getUserManager()->getLocalUser().getId();
}

/**
  * Called when the user wants to create a new document. Sends addition to server
  */
void DocumentManager::createDocument(Editor::Type type)
{
	addNewDocumentToList(type);

	//send the document to others
	if (session->isConnectedToServer())
	{
		DocumentMessage newMsg;
		newMsg.setDocId(docList.size());
		newMsg.setCreatorId(session->getUserManager()->getLocalUser().getId());
		newMsg.setTabName(docList[docList.size()-1]->getTabName());

		if (type == Editor::FORMATTED)
			newMsg.setDocType(DocumentMessage::FORMATTED);
		else
			newMsg.setDocType(DocumentMessage::TEXT);

		session->sendMessage(newMsg.toBytes());
	}
}

/**
  *	Locally creates a new document
  */
void DocumentManager::addNewDocumentToList(Editor::Type type, QString name)
{
	Editor *newEditor;

	if (type == Editor::FORMATTED)
		newEditor = new FormattedEditor(this);
	else
		newEditor = new TextEditor(this);

	newEditor->setDocId(docList.size());

	if(name.isEmpty())
	{
	    QString text = QInputDialog::getText(MainWindow::getInstance(), QObject::tr("Create a document"),
						QObject::tr("Name of document:"), QLineEdit::Normal, QString("Untitled"));
	    if (!text.isEmpty())
		newEditor->setTabName(text);
	}else{
	    newEditor->setTabName(name);
	}

	//add to doclist and main window
	docList.push_back(newEditor);
	(MainWindow::getInstance())->addEditorToTab(newEditor);	//add the tab to the mainwindow
}

void DocumentManager::loadDocument(QString filename)
{
	QFile file(filename);
	if(!file.open(QIODevice::ReadOnly))
	{
	    QMessageBox msg;
	    msg.setText("Could not open file " + filename);
	    msg.exec();
	    return;
	}

	QByteArray bytes = file.readAll();  //Get file in

	QMessageBox msgBox;
	int ret = -1;

	msgBox.setText("Is this document a formatted type document?");
	msgBox.setInformativeText("If you select 'No' a text document will be assumed.");
	msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
	msgBox.setDefaultButton(QMessageBox::Yes);
	ret = msgBox.exec();

	if(ret == QMessageBox::Yes)
	{
	    this->createDocument(Editor::FORMATTED);

	    WelcomeScreen::getInstance()->addRecentFile(filename, Editor::FORMATTED);
	}else{
	    this->createDocument(Editor::TEXT);

	    WelcomeScreen::getInstance()->addRecentFile(filename, Editor::TEXT);
	}

	//Load in file
	docList[docList.size()-1]->loadFile(bytes);

	//Send change message
	docList[docList.size()-1]->textChanged(0,0,bytes.capacity());
}

void DocumentManager::closeDocument(Editor* closeMe)
{
	if (!session->isConnectedToServer())
	{
		docList.removeAt(docList.indexOf(closeMe));
		delete closeMe;
	}
}

void DocumentManager::rehighlightAll()
{
	foreach(Editor* ed, docList)
	{
		bool previousSignalState = ed->document()->blockSignals(true);
		ed->getUserHighlighter()->rehighlight();
		ed->document()->blockSignals(previousSignalState);
	}
}

void DocumentManager::saveSession(QString filename)
{
    //Save out documents to file
    QList<Editor*>::iterator i;
    i=docList.begin();

    QFile output(filename+"_sessDat.wed");
    output.open(QIODevice::WriteOnly);
    QTextStream out(&output);

    //Save documentManager specific files
    out << QString::number(docList.size()) + "\n";

    for(int x=0; x<this->numEditors(); x++){
	Editor* curr = *i;

	out << curr->saveSession(filename+"_docMan_"+ QString::number(x)) + "\n";

	//Contains the int(docId), int(revisionNum), QString(tabName), QString(filename)|int(programmingLang), int(type);
	i++;
    }

    output.close();     //Close file
}

void DocumentManager::saveSession(QByteArray& myBytes)
{
    const char breakChar = 0x1D;

    //Save out documents to file
    QList<Editor*>::iterator i;
    i=docList.begin();

    QByteArray workerBytes;

    myBytes.append(QString::number(docList.size()) + "\n");

    for(int x=0; x<this->numEditors(); x++)
    {
	Editor* curr = *i;

	myBytes.append(curr->getMeta());	//Contains the int(docId), int(revisionNum), QString(tabName), QString(filename)|int(programmingLang), int(type);

	i++;
    }

    //Add in special split character
    myBytes.append(breakChar);

    //Get documents and user statistics pairs
    i=docList.begin();

    for(int x=0; x<this->numEditors(); x++)
    {
	    Editor* curr = *i;

	    curr->saveFile(workerBytes);    //Get the file data and add it to the QByteArray
	    myBytes.append(workerBytes);
	    myBytes.append(breakChar);	    //Add in the break char

	    curr->saveUserStats(workerBytes);	//Get the user statistics of that file and append to QByteArray
	    myBytes.append(workerBytes);
	    myBytes.append(breakChar);	    //Add in the break char

	    i++;
    }

    //QByteArray, myBytes, now contains the file metedata, and the file data/user statistic pairs for all of the open documents
    return;
}

void DocumentManager::loadSession(QString filename)
{
    //Open up documents and add them to the editor
    QFile input(filename+"_sessDat.wed");

    QList<Editor*>::iterator i;

    if(!input.open(QIODevice::ReadOnly | QIODevice::Text))      //Make sure file was opened successfuly
    {
	QMessageBox msgBox;
	msgBox.setText("Error: Unable to open session documents");
	msgBox.exec();

	return;
    }

    QTextStream in(&input);

    int documents = -1;
    documents = QString(in.readLine()).toInt();

    for(int x=0; x<documents;x++)   //Read in document metadata, check whether it is text or formatted, create editior and load file.
    {
	QString meta = "";
	meta = in.readLine();   //Reads in docId, revisionNum, tabName, filename or programming lang, and type.
	QStringList metaList = meta.split(":");

	//Check doc type
	QString type = metaList.at(4).toLocal8Bit().constData();
	if(type=="0")     //Text type document
	    this->addNewDocumentToList(Editor::TEXT, metaList.at(2).toLocal8Bit().constData());
	else                       //Formatted type document
	    this->addNewDocumentToList(Editor::FORMATTED, metaList.at(2).toLocal8Bit().constData());

	if(x==0)    //Set iterator for first document
	    i=docList.begin();
	else
	    i++;

	Editor* currDoc = *i;
	currDoc->loadSession(filename+"_docMan_"+ QString::number(x), metaList);    //Load up file and metadata
    }

    input.close();

}

void DocumentManager::loadSession(QByteArray& myBytes)
{
    const char breakChar = 0x1D;    //Group separator

    QList<Editor*>::iterator i;
    QString line = "";

    QList<QByteArray> myBytesList = myBytes.split(breakChar);

    QTextStream in(&myBytesList[0]);

    int documents = -1;
    documents = QString(in.readLine()).toInt();

    for(int x = 0; x < documents; x++)
    {
	QString line = "";
	line = in.readLine();   //Reads in docId, revisionNum, tabName, filename or programming lang, and type.
	QStringList meta = line.split(":");

	QString type = meta.at(4).toLocal8Bit().constData();
	if(type=="0")     //Text type document
	    this->addNewDocumentToList(Editor::TEXT, meta.at(2).toLocal8Bit().constData());
	else                       //Formatted type document
	    this->addNewDocumentToList(Editor::FORMATTED, meta.at(2).toLocal8Bit().constData());

	if(x==0)    //Set iterator for first document
	    i=docList.begin();
	else
	    i++;

	Editor* currDoc = *i;
	currDoc->setMeta(meta);				//Load up the meta data information
	currDoc->loadFile(myBytesList[(x*2)+1]);	//Load up the document
	currDoc->loadUserStats(myBytesList[(x*2)+2]);	//Load up the user statistics information
    }
}
